package com.note.gateway.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.note.common.vo.ResponseVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.WebProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.config.WebFluxConfigurer;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.*;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 网关全局错误处理配置
 */
@Slf4j
@Configuration
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
@ConditionalOnClass(WebFluxConfigurer.class)
@AutoConfigureBefore(ErrorWebFluxAutoConfiguration.class)
@EnableConfigurationProperties({ServerProperties.class, WebProperties.class})
public class GlobalErrorHandlerConfiguration {

    private final ServerProperties serverProperties;
    private final ApplicationContext applicationContext;
    private final ObjectProvider<List<ViewResolver>> viewResolversProvider;
    private final ServerCodecConfigurer serverCodecConfigurer;
    private final ObjectMapper objectMapper;

    public GlobalErrorHandlerConfiguration(
            ServerProperties serverProperties,
            ApplicationContext applicationContext,
            ObjectProvider<List<ViewResolver>> viewResolversProvider,
            ServerCodecConfigurer serverCodecConfigurer,
            ObjectMapper objectMapper) {
        this.serverProperties = serverProperties;
        this.applicationContext = applicationContext;
        this.viewResolversProvider = viewResolversProvider;
        this.serverCodecConfigurer = serverCodecConfigurer;
        this.objectMapper = objectMapper;
    }

    /**
     * 全局异常处理器
     */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public ErrorWebExceptionHandler globalErrorWebExceptionHandler(ErrorAttributes errorAttributes) {
        // 自定义异常处理
        GlobalErrorHandler exceptionHandler = new GlobalErrorHandler(
                errorAttributes,
                this.viewResolversProvider.getIfAvailable(Collections::emptyList),
                this.serverCodecConfigurer,
                this.objectMapper);

        // 设置异常处理器的属性
        exceptionHandler.setApplicationContext(this.applicationContext);
        exceptionHandler.setMessageWriters(this.serverCodecConfigurer.getWriters());
        exceptionHandler.setMessageReaders(this.serverCodecConfigurer.getReaders());

        return exceptionHandler;
    }

    /**
     * 自定义全局错误处理器
     */
    private static class GlobalErrorHandler implements ErrorWebExceptionHandler {

        private final ErrorAttributes errorAttributes;
        private final List<ViewResolver> viewResolvers;
        private final ServerCodecConfigurer serverCodecConfigurer;
        private final ObjectMapper objectMapper;
        private ApplicationContext applicationContext;

        public GlobalErrorHandler(
                ErrorAttributes errorAttributes,
                List<ViewResolver> viewResolvers,
                ServerCodecConfigurer serverCodecConfigurer,
                ObjectMapper objectMapper) {
            this.errorAttributes = errorAttributes;
            this.viewResolvers = viewResolvers;
            this.serverCodecConfigurer = serverCodecConfigurer;
            this.objectMapper = objectMapper;
        }

        public void setApplicationContext(ApplicationContext applicationContext) {
            this.applicationContext = applicationContext;
        }

        public void setMessageWriters(List messageWriters) {
            // 不需要任何操作
        }

        public void setMessageReaders(List messageReaders) {
            // 不需要任何操作
        }

        @Override
        public Mono<Void> handle(ServerWebExchange exchange, Throwable throwable) {
            // 获取HTTP状态
            HttpStatusCode statusCode = exchange.getResponse().getStatusCode();
            HttpStatus httpStatus;
            
            if (statusCode == null) {
                httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
            } else if (statusCode instanceof HttpStatus) {
                httpStatus = (HttpStatus) statusCode;
            } else {
                // 获取数字状态码
                int value = statusCode.value();
                // 尝试解析为HttpStatus枚举值
                httpStatus = HttpStatus.resolve(value);
                // 如果无法解析，设置为内部服务器错误
                if (httpStatus == null) {
                    httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
                }
            }

            // 设置响应的Content-Type
            exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
            exchange.getResponse().setStatusCode(httpStatus);

            // 构建响应数据
            return buildResponseBody(throwable, httpStatus)
                    .flatMap(responseBody -> {
                        byte[] bytes = responseBody.getBytes(StandardCharsets.UTF_8);
                        DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
                        return exchange.getResponse()
                                .writeWith(Mono.just(buffer));
                    });
        }

        /**
         * 构建响应体
         */
        private Mono<String> buildResponseBody(Throwable throwable, HttpStatus httpStatus) {
            Map<String, Object> result = new HashMap<>();
            
            // 根据不同的HTTP状态返回不同的错误信息
            ResponseVo<?> responseVo;
            
            if (httpStatus.is4xxClientError()) {
                if (httpStatus == HttpStatus.NOT_FOUND) {
                    responseVo = ResponseVo.error(httpStatus.value(), "请求的资源不存在");
                } else if (httpStatus == HttpStatus.UNAUTHORIZED) {
                    responseVo = ResponseVo.error(httpStatus.value(), "未授权访问");
                } else if (httpStatus == HttpStatus.FORBIDDEN) {
                    responseVo = ResponseVo.error(httpStatus.value(), "禁止访问");
                } else if (httpStatus == HttpStatus.METHOD_NOT_ALLOWED) {
                    responseVo = ResponseVo.error(httpStatus.value(), "请求方法不被允许");
                } else if (httpStatus == HttpStatus.BAD_REQUEST) {
                    responseVo = ResponseVo.error(httpStatus.value(), "错误的请求");
                } else {
                    responseVo = ResponseVo.error(httpStatus.value(), "客户端错误");
                }
            } else if (httpStatus.is5xxServerError()) {
                if (httpStatus == HttpStatus.SERVICE_UNAVAILABLE) {
                    responseVo = ResponseVo.error(httpStatus.value(), "服务不可用");
                } else if (httpStatus == HttpStatus.GATEWAY_TIMEOUT) {
                    responseVo = ResponseVo.error(httpStatus.value(), "网关超时");
                } else {
                    responseVo = ResponseVo.error(httpStatus.value(), "服务器内部错误");
                }
                log.error("服务器异常", throwable);
            } else {
                responseVo = ResponseVo.error(httpStatus.value(), "未知错误");
            }
            
            try {
                return Mono.just(objectMapper.writeValueAsString(responseVo));
            } catch (Exception e) {
                log.error("序列化响应对象失败", e);
                return Mono.just("{\"code\":" + httpStatus.value() + 
                        ",\"status\":false,\"message\":\"" + httpStatus.getReasonPhrase() + "\"}");
            }
        }
    }
} 